﻿using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using Greg.XmlEditor.Business;
using Greg.XmlEditor.Presentation.Commands;
using Greg.XmlEditor.Presentation.Commands.Concrete;

namespace Greg.XmlEditor.Presentation.Presenters
{
	public class MasterPresenter : PresenterBase
	{
		private readonly IDocumentPresenterFactory factory;
		private readonly IValidationService validationService;
		private readonly IInteractionManager interaction;
		private string lastValidatedXml;

		public MasterPresenter(IDocumentPresenterFactory factory, IValidationService validationService, ITransformationService transformationService, IInteractionManager interaction)
		{
			this.factory = factory;
			this.validationService = validationService;
			this.interaction = interaction;
			this.Documents = new ObservableCollection<DocumentPresenter>();

			this.NewCommand = new NewCommand(this, factory);
			this.OpenCommand = new OpenCommand(this, interaction);
			this.CloseCommand = new CloseCommand(this);

			this.SaveAsCommand = new SaveAsCommand(this, interaction);
			this.SaveCommand = new SaveCommand(this, this.SaveAsCommand, interaction);
			this.SaveAllCommand = new SaveAllCommand(this, this.SaveCommand, interaction);

			this.OpenStyleCommand = new OpenStyleCommand(this, interaction);
			this.OpenSchemaCommand = new OpenSchemaCommand(this, interaction);

			this.ApplyStyleCommand = new ApplyStyleCommand(this, transformationService, interaction);

			this.ExitCommand = new ExitCommand(this, interaction);

			Application.Idle += (s, e) => Validate();
		}

		#region Bindable properties

		public ObservableCollection<DocumentPresenter> Documents { get; private set; }

		public DocumentPresenterBase CurrentDocument
		{
			get { return this.GetValue<DocumentPresenterBase>("CurrentDocument"); }
			set { this.SetValue("CurrentDocument", value); }
		}

		public string Output
		{
			get { return this.GetValue<string>("Output"); }
			set { this.SetValue("Output", value); }
		}

		#endregion

		protected override void OnPropertyChanged(string propertyName)
		{
			base.OnPropertyChanged(propertyName);
			switch (propertyName)
			{
				case "CurrentDocument":
					this.lastValidatedXml = null;
					this.Output = string.Empty;
					this.CloseCommand.Refresh();
					this.SaveCommand.Refresh();
					this.SaveAsCommand.Refresh();
					this.SaveAllCommand.Refresh();
					this.OpenSchemaCommand.Refresh();
					this.OpenStyleCommand.Refresh();
					this.ApplyStyleCommand.Refresh();
					break;
			}
		}

		public void Validate()
		{
			ThreadPool.QueueUserWorkItem(x =>
			{
				if (this.CurrentDocument == null) return;

				if (this.CurrentDocument is DocumentPresenter)
				{
					var document = (DocumentPresenter)this.CurrentDocument;
					if (document.Text == this.lastValidatedXml) return;

					if (string.IsNullOrEmpty(document.Text) || document.Text.Trim().Length == 0)
					{
						this.Output = "Empty document";
						return;
					}

					this.lastValidatedXml = document.Text;

					var result = !document.Schema.IsPersistent ?
						this.validationService.Validate(document.Text) :
						this.validationService.Validate(document.Text, document.Schema.FullPath);

					document.Output = string.Format("Validation {0}: {1}", result.Succeeded ? "OK" : "ERROR", result.Message);
				}
				else
				{
					var document = this.CurrentDocument;
					if (document.Text == this.lastValidatedXml) return;

					if (string.IsNullOrEmpty(document.Text) || document.Text.Trim().Length == 0)
					{
						this.Output = "Empty document";
						return;
					}

					this.lastValidatedXml = document.Text;
					var result = this.validationService.Validate(document.Text);

					document.Output = string.Format("Validation {0}: {1}", result.Succeeded ? "OK" : "ERROR", result.Message);
				}
			});
		}

		public void Open(string fileName)
		{
			if (this.Documents.Any(d => d.FullPath == fileName))
			{
				this.interaction.ShowError("The file is already open.");
				return;
			}

			var document = this.factory.LoadDocument(fileName);
			this.Documents.Add(document);
			this.CurrentDocument = document;
		}

		#region Command definitions

		public ICommand NewCommand { get; private set; }
		public ICommand OpenCommand { get; private set; }
		public ICommand CloseCommand { get; private set; }

		public ICommand SaveCommand { get; private set; }
		public ICommand SaveAsCommand { get; private set; }
		public ICommand SaveAllCommand { get; private set; }

		public ICommand OpenStyleCommand { get; private set; }
		public ICommand OpenSchemaCommand { get; private set; }

		public ICommand ExitCommand { get; private set; }

		public ICommand ApplyStyleCommand { get; private set; }

		#endregion		
	}
}
